package com.ruoyi.web.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.AES;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.framework.web.enums.AbnormalEnum;
import com.ruoyi.framework.web.exception.TadpoleException;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.mapper.SysUserRoleMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.constant.WalletConstant;
import com.ruoyi.web.domain.*;
import com.ruoyi.web.dto.*;
import com.ruoyi.web.enums.CommonDictEnum;
import com.ruoyi.web.enums.RechargeRecordEnum;
import com.ruoyi.web.enums.UserEnums;
import com.ruoyi.web.mapper.*;
import com.ruoyi.web.service.IAppUserService;
import com.ruoyi.web.service.IUserAddressService;
import com.ruoyi.web.service.IUserCapitalService;
import com.ruoyi.web.utils.CommonUtil;
import com.ruoyi.web.utils.IdWorker;
import com.ruoyi.web.utils.MysqlUtil;
import com.ruoyi.web.utils.TokenUtil;
import com.ruoyi.web.utils.qg_pay.utils.QGPayUtil;
import com.ruoyi.web.vo.*;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.tron.trident.core.key.KeyPair;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * app用户Service业务层处理
 *
 * @author ruoyi
 * @date 2022-12-28
 */
@Service
@AllArgsConstructor
public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements IAppUserService {
    private final AppUserMapper appUserMapper;
    private final ISysUserService userService;
    private final SysLoginService loginService;
    private final UserAddressMapper addressMapper;
    private final UserCapitalMapper capitalMapper;
    private final IdWorker idWorker;
    private final IUserAddressService addressService;
    private final IUserCapitalService capitalService;
    private final UserJobConfigMapper configMapper;
    private final UserJobRecordMapper recordMapper;
    private final RechargeRecordMapper rechargeRecordMapper;
    private final MysqlUtil mysqlUtil;
    private final StringRedisTemplate stringRedisTemplate;
    private final UserEmpowerMapper empowerMapper;
    private final UserEmpowerConfigMapper empowerConfigMapper;
    private final UserProfitRecordMapper profitRecordMapper;
    private final MiningConfigMapper miningConfigMapper;
    private final SysUserRoleMapper sysUserRoleMapper;
    private final TokenService tokenService;
    private final CommonUtil commonUtil;
    private final ISysConfigService sysConfigService;
    private final QGPayUtil qgPayUtil;

    @Override
    public R<AppUserDetailVO> getAppUserDetail() {
        try {
            Long userId = SecurityUtils.getUserId();
            if (userId == null) {
                throw new TadpoleException(AbnormalEnum.MSG.OPTIONS_USER_NOT_FOUND);
            }
            AppUserDetailVO vo = new AppUserDetailVO();
            try {
                // 获取充值地址
                List<UserAddressVO> data = addressService.getUserAddress().getData();
                vo.setRechargeAddress(data);
            } catch (Exception e) {
                log.error("用户 " + userId + ": 获取充值地址异常 | error:" + e);
            }
            try {
                // 用户基本信息
                AppUser appUser = appUserMapper.selectAppUserById(userId);
                BeanUtils.copyProperties(appUser, vo);
                vo.setLevel(appUser.getVipLevel());
            } catch (Exception e) {
                log.error("用户 " + userId + ": 获取基本信息异常 | error:" + e);
            }
            try {
                // 资金余额
                UserCapital capital = capitalService.selectByUserId(userId);
                vo.setBalance(capital.getBalance());
                vo.setVipDeposit(capital.getVipDeposit());
            } catch (Exception e) {
                log.error("用户 " + userId + ": 获取资金余额异常 | error:" + e);
            }
            try {
                // 查询用户任务数据
                UserJobConfig config = configMapper.selectOne(new UserJobConfig() {{
                    setLevel(vo.level.longValue());
                }});
                if (config != null) {
                    Date now = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    String nowStr = sdf.format(now) + " 00:00:00";
                    UserDoJobVO jobCount = recordMapper.selectDoJobCount(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(nowStr), userId);
                    vo.setJobCount(config.getCount().intValue() - jobCount.getCount().intValue());
                    vo.setTodayReward(jobCount.getReward());
                } else {
                    vo.setJobCount(0);
                    vo.setTodayReward(BigDecimal.ZERO);
                }
            } catch (Exception e) {
                log.error("用户 " + userId + ": 获取任务相关数据异常 | error:" + e);
            }
            try {
                // 质押等级
                List<UserEmpower> empowerList = empowerMapper.selectUserEmpowerList(new UserEmpower() {{
                    setUserId(userId);
                }});
                if (empowerList.size() > 0) {
                    UserEmpower empower = empowerList.get(0);
                    UserEmpowerConfig config = empowerConfigMapper.selectUserConfig(empower.getNum());
                    vo.setMiningLevel(config.getLevel().intValue());
                } else {
                    vo.setMiningLevel(0);
                }
            } catch (Exception e) {
                log.error("用户 " + userId + ": 获取质押等级异常 | error:" + e);
            }

            try {
                List<MiningConfig> configs = miningConfigMapper.selectMiningConfigList(new MiningConfig());
                if (configs.size() > 0) {
                    MiningConfig miningConfig = configs.get(0);
                    vo.setEthEmpowerAddress(miningConfig.getEthAddress());
                    vo.setTrxEmpowerAddress(miningConfig.getTrxAddress());
                    vo.setBscEmpowerAddress(miningConfig.getBscAddress());
                }
            } catch (Exception e) {
                log.error("用户 " + userId + ": 获取授权地址异常 | error:" + e);
            }

            // 法币提现手续费率
            vo.setLegalTenderWithdrawalFeeRate(qgPayUtil.getWithdrawalFeeRate());

            return R.ok(vo);
        } catch (TadpoleException exception) {
            throw exception;
        } catch (Exception e) {
            throw new TadpoleException(AbnormalEnum.MSG.DISCONNECTED);
        }
    }

    /**
     * 代理人账号启用/停用
     *
     * @param userId  appUser id
     * @param isAgent 是否代理人
     * @since 2023/3/21 17:19
     */
    public void handleAppUserIsAgent(Long userId, String isAgent) {
        AppUser appUser = appUserMapper.selectAppUserById(userId);
        if (Objects.isNull(appUser)) {
            throw new TadpoleException("H5用户数据为空");
        }

        // 是否代理人没有变化
        if (isAgent.equals(appUser.getIsAgent())) {
            return;
        }

        // 查询代理人
        SysUser sysUser = userService.selectUserById(userId);
        // 原先为否，开启代理人
        if (CommonDictEnum.YesOrNo.NO.getValue().equals(appUser.getIsAgent())) {
            // 如果没有，则新增
            if (Objects.isNull(sysUser)) {
                sysUser = new SysUser();

                sysUser.setUserId(appUser.getId());
                sysUser.setUserName(appUser.getInvitationCode());
                sysUser.setNickName(appUser.getInvitationCode());
                sysUser.setPassword(SecurityUtils.encryptPassword(appUser.getInvitationCode()));
                sysUser.setStatus("0");
                sysUser.setRemark(Constants.SYS_USER_REMARK_AGENT);
                if (userService.insertUser(sysUser) != 1) {
                    throw new TadpoleException("创建代理账户失败");
                }
            } else {
                sysUser.setStatus("0");
                if (userService.updateUserStatus(sysUser) != 1) {
                    throw new TadpoleException("启用代理账户失败");
                }
            }

            // 添加代理人角色
            SysUserRole role = new SysUserRole();
            role.setUserId(sysUser.getUserId());
            role.setRoleId(3L);
            if (sysUserRoleMapper.batchUserRole(Collections.singletonList(role)) != 1) {
                throw new TadpoleException("创建代理账户失败");
            }

            // 原先为是，关闭代理人
        } else if (CommonDictEnum.YesOrNo.YES.getValue().equals(appUser.getIsAgent())) {
            if (Objects.isNull(sysUser)) {
                throw new TadpoleException("数据错误，未能查询到后台代理人账户");
            }

            // 停用账户
            sysUser.setStatus("1");
            if (userService.updateUserStatus(sysUser) != 1) {
                throw new TadpoleException("停用代理账户失败");
            }

            // 删除代理人角色
            SysUserRole role = new SysUserRole();
            role.setUserId(sysUser.getUserId());
            role.setRoleId(3L);
            if (sysUserRoleMapper.deleteUserRoleInfo(role) != 1) {
                throw new TadpoleException("停用代理账户失败");
            }
        }
    }

    @Override
    public R<String> updateEmail(UpdateUserDto dto) {
        Long userId = SecurityUtils.getUserId();
        AppUser appUser = appUserMapper.selectAppUserById(userId);
        if (appUser == null) {
            throw new TadpoleException(AbnormalEnum.MSG.OPTIONS_USER_NOT_FOUND);
        }
        AppUser emailUser = appUserMapper.selectOne(new AppUser() {{
            setEmail(dto.getEmail());
        }});
        if (emailUser != null && emailUser.getEmail().equals(dto.getEmail())) {
            throw new TadpoleException(AbnormalEnum.MSG.UNSUPPORTED_OPERATION.getCode(), "This mailbox has been registered");
        }
        // 邮箱验证码
        String key = appUser.getEmail() + " massage = " + UserEnums.EmailType.Customer_Modify_Email.getType();
        String codeRedis = stringRedisTemplate.opsForValue().get(key);
        if (!StrUtil.equals(dto.getVerificationCode(), codeRedis)) {
            throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN.getCode(), "Verification code error");
        }
        appUser.setEmail(dto.getEmail());
        try {
            appUserMapper.updateAppUser(appUser);
            stringRedisTemplate.delete(key);
        } catch (Exception e) {
            throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN_ORDER_COMPOSITION.getCode(), "Modification failed");
        }
        return R.ok();
    }

    /**
     * 生成地址
     */
    private void insertUserAddress(Long userId) {
        AES aes = new AES(WalletConstant.AES_KEY);
        // 账户 (eth)
        ECKeyPair ecKeyPair;
        try {
            ecKeyPair = Keys.createEcKeyPair();
        } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
        // 公钥
        String ethPublicKey = ecKeyPair.getPublicKey().toString(16);
        // 私钥
        String ethPrivateKey = ecKeyPair.getPrivateKey().toString(16);
        // 地址
        String ethAddress = "0x" + Keys.getAddress(ethPublicKey);
        // 加密
        String ethAesPrivateKey = aes.encryptBase64(ethPrivateKey, Charset.defaultCharset());
        addressMapper.insertUserAddress(new UserAddress() {{
            setId(commonUtil.getId());
            setUserId(userId);
            setCreateTime(LocalDateTime.now());
            setUpdateTime(LocalDateTime.now());
            setAddress(ethAddress);
            setPrivateKey(ethAesPrivateKey);
            setChain(UserEnums.Chain.ETH.getValue());
        }});

        // 账户 (bsc)
        ecKeyPair = null;
        try {
            ecKeyPair = Keys.createEcKeyPair();
        } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
        // 公钥
        String bscPublicKey = ecKeyPair.getPublicKey().toString(16);
        // 私钥
        String bscPrivateKey = ecKeyPair.getPrivateKey().toString(16);
        // 地址
        String bscAddress = "0x" + Keys.getAddress(bscPublicKey);
        // 加密
        String bscAesPrivateKey = aes.encryptBase64(bscPrivateKey, Charset.defaultCharset());
        addressMapper.insertUserAddress(new UserAddress() {{
            setId(commonUtil.getId());
            setUserId(userId);
            setAddress(bscAddress);
            setCreateTime(LocalDateTime.now());
            setUpdateTime(LocalDateTime.now());
            setPrivateKey(bscAesPrivateKey);
            setChain(UserEnums.Chain.BSC.getValue());
        }});

        // 账户（TRX）
        KeyPair keyPair = KeyPair.generate();
        // 私钥
        String tronPrivateKey = keyPair.toPrivateKey();
        // 地址
        String tronAddress = keyPair.toBase58CheckAddress();
        // 加密
        String tronAesPrivateKey = aes.encryptBase64(tronPrivateKey, Charset.defaultCharset());
        addressMapper.insertUserAddress(new UserAddress() {{
            setId(commonUtil.getId());
            setUserId(userId);
            setCreateTime(LocalDateTime.now());
            setUpdateTime(LocalDateTime.now());
            setAddress(tronAddress);
            setPrivateKey(tronAesPrivateKey);
            setChain(UserEnums.Chain.TRX.getValue());
        }});
    }

    /**
     * 生成资金账户
     */
    private void insertUserCapital(Long userId) {
        capitalMapper.insertUserCapital(new UserCapital() {{
            setId(commonUtil.getId());
            setUserId(userId);
            setBalance(BigDecimal.ZERO);
            setCurrency(WalletConstant.USDT);
            setVipDeposit(BigDecimal.ZERO);
            setCreateTime(LocalDateTime.now());
            setUpdateTime(LocalDateTime.now());
        }});
    }

    @Override
    public void rechargeDistribution(RechargeRecord record) {
        AppUser user = appUserMapper.selectAppUserById(record.getUserId());
        List<AppUser> parentList = getParentList(user.getParentId());
        if (parentList.size() > 0) {
            AppUser parent = parentList.get(0);
            // 直推获取充值10%
            BigDecimal quantity = record.getQuantity().multiply(new BigDecimal("0.1"));
            capitalMapper.updateUserBalance(quantity, parent.getId());
            RechargeRecord rechargeRecord = new RechargeRecord();
            rechargeRecord.setId(commonUtil.getId());
            rechargeRecord.setUserId(parent.getId());
            rechargeRecord.setCurrency(WalletConstant.USDT);
            rechargeRecord.setType(RechargeRecordEnum.Type.RECHARGE_DISTRIBUTION.getValue());
            rechargeRecord.setState(RechargeRecordEnum.Status.SUCCESS.getValue());
            rechargeRecord.setQuantity(quantity);
            rechargeRecordMapper.insertRechargeRecord(rechargeRecord);
        }
    }

    /**
     * 充值分红
     *
     * @param userId 用户id
     * @param amount 金额
     * @since 2023/3/17 15:01
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void rechargeDistribution(Long userId, BigDecimal amount) {
        AppUser user = appUserMapper.selectAppUserById(userId);
        if (Objects.isNull(user.getParentId()) || user.getParentId() == 1) {
            return;
        }

        List<AppUser> parentList = getParentList(user.getParentId());
        if (parentList.size() > 0) {
            AppUser parent = parentList.get(0);
            // 直推获取充值10%
            BigDecimal quantity = amount.multiply(new BigDecimal("0.1"));
            if (capitalMapper.addBalance(parent.getId(), quantity) != 1) {
                throw new TadpoleException();
            }
            RechargeRecord rechargeRecord = new RechargeRecord();
            rechargeRecord.setId(commonUtil.getId());
            rechargeRecord.setUserId(parent.getId());
            rechargeRecord.setCurrency(WalletConstant.USDT);
            rechargeRecord.setType(RechargeRecordEnum.Type.RECHARGE_DISTRIBUTION.getValue());
            rechargeRecord.setState(RechargeRecordEnum.Status.SUCCESS.getValue());
            rechargeRecord.setQuantity(quantity);
            if (rechargeRecordMapper.insertRechargeRecord(rechargeRecord) != 1) {
                throw new TadpoleException();
            }
        }
    }

    @Override
    public void miningDistribution(RechargeRecord record, String type) {
        try {
            // 获取父级
            AppUser user = appUserMapper.selectAppUserById(record.getUserId());
            List<AppUser> parentList = getParentList(user.getParentId());
            mysqlUtil.startLocalTx();
            if (parentList.size() > 0) {
                // 一级关系获取7%
                AppUser parent1 = parentList.get(0);
                BigDecimal proportion = new BigDecimal("0.07");
                BigDecimal multiply = record.getQuantity().multiply(proportion);
                capitalMapper.updateUserBalance(multiply, parent1.getId());
                RechargeRecord rechargeRecord = new RechargeRecord();
                rechargeRecord.setId(commonUtil.getId());
                rechargeRecord.setUserId(parent1.getId());
                rechargeRecord.setCurrency(WalletConstant.USDT);
                rechargeRecord.setType(type);
                rechargeRecord.setState(RechargeRecordEnum.Status.SUCCESS.getValue());
                rechargeRecord.setQuantity(multiply);
                rechargeRecordMapper.insertRechargeRecord(rechargeRecord);
                UserProfitRecord profitRecord = new UserProfitRecord();
                profitRecord.setToUser(parent1.getId());
                profitRecord.setCurrency(WalletConstant.USDT);
                profitRecord.setId(commonUtil.getId());
                profitRecord.setQuantity(multiply);
                profitRecord.setFromUser(record.getUserId());
                profitRecord.setPublicChain(record.getPublicChain());
                profitRecord.setType(type);
                profitRecordMapper.insertUserProfitRecord(profitRecord);
            }
            if (parentList.size() > 1) {
                // 二级关系获取5%
                AppUser parent2 = parentList.get(1);
                BigDecimal proportion = new BigDecimal("0.05");
                BigDecimal multiply = record.getQuantity().multiply(proportion);
                capitalMapper.updateUserBalance(multiply, parent2.getId());
                RechargeRecord rechargeRecord = new RechargeRecord();
                rechargeRecord.setId(commonUtil.getId());
                rechargeRecord.setUserId(parent2.getId());
                rechargeRecord.setCurrency(WalletConstant.USDT);
                rechargeRecord.setType(type);
                rechargeRecord.setState(RechargeRecordEnum.Status.SUCCESS.getValue());
                rechargeRecord.setQuantity(multiply);
                rechargeRecordMapper.insertRechargeRecord(rechargeRecord);
                UserProfitRecord profitRecord = new UserProfitRecord();
                profitRecord.setToUser(parent2.getId());
                profitRecord.setCurrency(WalletConstant.USDT);
                profitRecord.setId(commonUtil.getId());
                profitRecord.setQuantity(multiply);
                profitRecord.setFromUser(record.getUserId());
                profitRecord.setPublicChain(record.getPublicChain());
                profitRecord.setType(type);
                profitRecordMapper.insertUserProfitRecord(profitRecord);
            }
            if (parentList.size() > 2) {
                // 三级关系获取3%
                AppUser parent3 = parentList.get(1);
                BigDecimal proportion = new BigDecimal("0.03");
                BigDecimal multiply = record.getQuantity().multiply(proportion);
                capitalMapper.updateUserBalance(multiply, parent3.getId());
                RechargeRecord rechargeRecord = new RechargeRecord();
                rechargeRecord.setId(commonUtil.getId());
                rechargeRecord.setUserId(parent3.getId());
                rechargeRecord.setCurrency(WalletConstant.USDT);
                rechargeRecord.setType(type);
                rechargeRecord.setState(RechargeRecordEnum.Status.SUCCESS.getValue());
                rechargeRecord.setQuantity(multiply);
                rechargeRecordMapper.insertRechargeRecord(rechargeRecord);
                UserProfitRecord profitRecord = new UserProfitRecord();
                profitRecord.setToUser(parent3.getId());
                profitRecord.setCurrency(WalletConstant.USDT);
                profitRecord.setId(commonUtil.getId());
                profitRecord.setQuantity(multiply);
                profitRecord.setFromUser(record.getUserId());
                profitRecord.setPublicChain(record.getPublicChain());
                profitRecord.setType(type);
                profitRecordMapper.insertUserProfitRecord(profitRecord);
            }
            mysqlUtil.commitLocalTx();
        } catch (Exception e) {
            mysqlUtil.rollbackLocalTx();
        }
    }

    public List<AppUser> getParentList(Long parentId) {
        List<AppUser> list = new ArrayList<>();
        getParent(list, parentId);
        return list;
    }

    public void getParent(List<AppUser> list, Long parentId) {
        AppUser appUser = appUserMapper.selectOne(new AppUser() {{
            setId(parentId);
        }});
        if (appUser != null) {
            list.add(appUser);
            if (appUser.getParentId() != null) {
                getParent(list, appUser.getParentId());
            }
        }
    }

    /**
     * 获取所有子级
     *
     * @param list list
     * @param id   id
     */
    @Override
    public void getAllChild(List<AppUser> list, Long id) {
        List<AppUser> userList = appUserMapper.selectAppUserList(new AppUser() {{
            setParentId(id);
        }});
        if (userList.size() > 0) {
            list.addAll(userList);
            for (AppUser user : userList) {
                getAllChild(list, user.getId());
            }
        }
    }


    /**
     * 查询app用户
     *
     * @param id app用户主键
     * @return app用户
     */
    @Override
    public AppUser selectAppUserById(Long id) {
        return appUserMapper.selectAppUserById(id);
    }

    /**
     * 查询app用户列表
     *
     * @param appUser app用户
     * @return app用户
     */
    @Override
    public List<AppUser> selectAppUserList(AppUser appUser) {
        return appUserMapper.selectAppUserList(appUser);
    }

    /**
     * 新增app用户
     *
     * @param appUser app用户
     * @return 结果
     */
    @Override
    public int insertAppUser(AppUser appUser) {
        appUser.setCreateTime(LocalDateTime.now());
        return appUserMapper.insertAppUser(appUser);
    }

    /**
     * 修改app用户
     *
     * @param appUser app用户
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public int updateAppUser(AppUser appUser) {
        appUser.setUpdateTime(LocalDateTime.now());
        if (Strings.isNotBlank(appUser.getIsAgent())) {
            handleAppUserIsAgent(appUser.getId(), appUser.getIsAgent());
        }

        if (appUserMapper.updateAppUser(appUser) != 1) {
            throw new TadpoleException("修改用户失败");
        }

        return 1;
    }

    /**
     * 批量删除app用户
     *
     * @param ids 需要删除的app用户主键
     * @return 结果
     */
    @Override
    public int deleteAppUserByIds(Long[] ids) {
        return appUserMapper.deleteAppUserByIds(ids);
    }

    /**
     * 删除app用户信息
     *
     * @param id app用户主键
     * @return 结果
     */
    @Override
    public int deleteAppUserById(Long id) {
        return appUserMapper.deleteAppUserById(id);
    }

    /**
     * 注册
     *
     * @param dto dto
     * @return {@link AppLoginResVO}
     * @since 2023/3/21 14:57
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public AppLoginResVO register(AppUserRegisterDTO dto) {
        // 公链统一转换小写
        if (Strings.isNotBlank(dto.getChain())) {
            dto.setChain(dto.getChain().toLowerCase());
            if (Strings.isBlank(UserEnums.Chain.findName(dto.getChain()))) {
                throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN_ORDER_COMPOSITION.getCode(), "Unsupported public chain");
            }
        }

        // 根据手机号、公链和地址、邀请码查询重复的用户信息
        AppUser existUser = appUserMapper.checkConflict(dto.getPhone(),
                dto.getChain(),
                dto.getAddress(),
                null);
        if (!Objects.isNull(existUser)) {
            if (Objects.equals(dto.getPhone(), existUser.getPhone())) {
                throw new TadpoleException(AbnormalEnum.MSG.THIS_PHONE_NUMBER_HAS_ALREADY_REGISTERED);
            } else if (Objects.equals(dto.getChain(), existUser.getChain())
                    && Objects.equals(dto.getAddress(), existUser.getAddress())) {
                throw new TadpoleException(AbnormalEnum.MSG.THIS_ADDRESS_HAS_ALREADY_REGISTERED);
            }
        }

        // 根据邀请码查询上级用户
        AppUser parentUser = appUserMapper.selectOne(new AppUser() {{
            setInvitationCode(dto.getInvitationCode());
        }});
        if (Objects.isNull(parentUser)) {
            throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN_ORDER_COMPOSITION.getCode(), "Invalid invitation code");
        }

        // 生成六位随机数 邀请码
        String invitationCode = null;
        boolean isConflictInvitationCode = true;
        while (isConflictInvitationCode) {
            // 判断新生成的邀请码是否存在重复
            invitationCode = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));
            isConflictInvitationCode = appUserMapper.countConflict(null, null, null, invitationCode) != 0;
        }

        // 生成用户信息
        AppUser appUser = new AppUser();
        appUser.setId(commonUtil.getId());
        appUser.setParentId(parentUser.getId());
        appUser.setPhone(dto.getPhone());
        appUser.setEmail("");
        if (Strings.isBlank(dto.getAddress())) {
            dto.setChain(null);
            dto.setAddress(null);
        } else {
            appUser.setChain(dto.getChain());
            appUser.setAddress(dto.getAddress());
        }
        appUser.setPassword(dto.getPassword());
        appUser.setInvitationCode(invitationCode);
        appUser.setCreateTime(LocalDateTime.now());
        appUser.setUpdateTime(appUser.getCreateTime());
        appUser.setIsAgent(CommonDictEnum.YesOrNo.NO.getValue());
        appUser.setVipLevel(0);
        if (appUserMapper.register(appUser) != 1) {
            throw new TadpoleException(AbnormalEnum.MSG.UNKNOWN);
        }

        // 生成地址
        insertUserAddress(appUser.getId());
        // 生成资金账户
        insertUserCapital(appUser.getId());

        // 生成token
        String toke = tokenService.createToken(TokenUtil.createLoginUser(appUser));
        AppLoginResVO res = new AppLoginResVO();
        res.setToken(toke);

        R<AppUserDetailVO> detailR = getAppUserDetail();
        if (R.isSuccess(detailR)) {
            res.setUser(detailR.getData());
        }

        return res;
    }

    /**
     * 通过公链登录
     *
     * @param dto dto
     * @return {@link AppLoginResVO}
     * @since 2023/3/20 15:14
     */
    @Override
    public AppLoginResVO loginByAddress(AppUserLoginAddressDTO dto) {
        // 根据公链、地址查询用户
        AppUser condition = new AppUser();
        condition.setChain(dto.getChain());
        condition.setAddress(dto.getAddress());
        AppUser appUser = appUserMapper.selectOne(condition);
        // 通过地址进行自动登录时，如果找不到用户，不抛出异常。为了让h5页面不弹出错误提示
        if (Objects.isNull(appUser)) {
            return new AppLoginResVO();
        }

        // 生成token
        String toke = tokenService.createToken(TokenUtil.createLoginUser(appUser));
        AppLoginResVO res = new AppLoginResVO();
        res.setToken(toke);

        R<AppUserDetailVO> detailR = getAppUserDetail();
        if (R.isSuccess(detailR)) {
            res.setUser(detailR.getData());
        }

        return res;
    }

    /**
     * 通过手机号、密码登陆
     *
     * @param dto dto
     * @return {@link AppLoginResVO}
     * @since 2023/3/20 18:21
     */
    @Override
    public AppLoginResVO loginByPassword(AppUserLoginPasswordDTO dto) {
        // 根据手机号查询用户
        AppUser condition = new AppUser();
        condition.setPhone(dto.getPhone());
        AppUser appUser = appUserMapper.selectOne(condition);
        if (Objects.isNull(appUser)) {
            throw new TadpoleException(AbnormalEnum.MSG.OPTIONS_USER_NOT_FOUND);
        }

        // MD5加密传入的密码
        String hash = Md5Utils.hash(dto.getPassword());
        if (!hash.equals(appUser.getPassword())) {
            throw new TadpoleException(AbnormalEnum.MSG.INCORRECT_USERNAME_OR_PASSWORD);
        }

        // 生成token
        String toke = tokenService.createToken(TokenUtil.createLoginUser(appUser));
        AppLoginResVO res = new AppLoginResVO();
        res.setToken(toke);

        R<AppUserDetailVO> detailR = getAppUserDetail();
        if (R.isSuccess(detailR)) {
            res.setUser(detailR.getData());
        }

        return res;
    }

    /**
     * 旧用户完善信息
     *
     * @param dto dto
     * @return {@link AppUserDetailVO}
     * @since 2023/3/20 18:08
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public AppUserDetailVO complete(AppUserCompleteDTO dto) {
        SysUser user = CommonUtil.getLoginUserNotNull();

        // 根据手机号查询用户
        AppUser condition = new AppUser();
        condition.setPhone(dto.getPhone());
        AppUser appUser = appUserMapper.selectOne(condition);
        if (!Objects.isNull(appUser)) {
            throw new TadpoleException(AbnormalEnum.MSG.THIS_PHONE_NUMBER_HAS_ALREADY_REGISTERED);
        }

        condition.setId(user.getUserId());
        condition.setPhone(dto.getPhone());
        condition.setPassword(dto.getPassword());
        if (appUserMapper.complete(condition) != 1) {
            throw new TadpoleException(AbnormalEnum.MSG.THIS_PHONE_NUMBER_HAS_ALREADY_REGISTERED);
        }

        R<AppUserDetailVO> detailR = getAppUserDetail();
        return detailR.getData();
    }

    /**
     * 浏览器注册用户绑定地址
     *
     * @param dto dto
     * @return {@link AppUserDetailVO}
     * @since 2023/3/21 15:31
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public AppUserDetailVO addressBind(AppUserAddressBindDTO dto) {
        SysUser user = CommonUtil.getLoginUserNotNull();

        // 根据公链、地址查询用户
        AppUser condition = new AppUser();
        condition.setChain(dto.getChain());
        condition.setAddress(dto.getAddress());
        AppUser appUser = appUserMapper.selectOne(condition);
        if (!Objects.isNull(appUser)) {
            throw new TadpoleException(AbnormalEnum.MSG.THIS_ADDRESS_HAS_ALREADY_REGISTERED);
        }

        condition.setId(user.getUserId());
        if (appUserMapper.addressBind(condition) != 1) {
            throw new TadpoleException(AbnormalEnum.MSG.THIS_ADDRESS_HAS_ALREADY_REGISTERED);
        }

        R<AppUserDetailVO> detailR = getAppUserDetail();
        return detailR.getData();
    }

    /**
     * 绑定身份证信息
     *
     * @param dto dto
     * @since 2023/3/29 13:47
     */
    @Override
    public void bindIdentity(AppUserBindIdentityDTO dto) {
        SysUser user = CommonUtil.getLoginUserNotNull();
        AppUser condition = new AppUser();
        condition.setId(user.getUserId());
        condition.setName(dto.getName());
        condition.setIdentityNumber(dto.getIdentityNumber());
        if (appUserMapper.updateAppUser(condition) != 1) {
            CommonUtil.throwError();
        }
    }

    /**
     * 查询下级用户
     *
     * @return {@link List<  AppUserVO >}
     * @since 2023/3/29 14:36
     */
    @Override
    public List<AppUserVO> appListChild() {
        SysUser user = CommonUtil.getLoginUserNotNull();
        Long userId = user.getUserId();
        AppUser appUser = appUserMapper.selectAppUserById(userId);
        if (Objects.isNull(appUser)) {
            CommonUtil.throwError();
        }

        AppUserVO vo = new AppUserVO();
        BeanUtils.copyProperties(appUser, vo);
        ArrayList<AppUserVO> list = new ArrayList<>();

        listChild(list, vo, 1, 3);

        return list;
    }

    /**
     * 查询下级用户
     *
     * @param list     集合
     * @param parent   上级用户
     * @param level    当前层级
     * @param maxLevel 最大层级
     * @since 2023/3/29 15:02
     */
    private void listChild(List<AppUserVO> list, AppUserVO parent, int level, int maxLevel) {
        // 根据parentId查询数据
        List<AppUser> currentList = appUserMapper.selectAppUserList(new AppUser() {{
            setParentId(parent.getId());
        }});

        if (currentList.isEmpty()) {
            parent.setInvite1Num(0);
            return;
        }

        parent.setInvite1Num(currentList.size());
        List<AppUserVO> voList = currentList.stream()
                .map(i -> {
                    AppUserVO vo = new AppUserVO();
                    BeanUtils.copyProperties(i, vo);
                    vo.setInviteLevel(level);
                    return vo;
                }).collect(Collectors.toList());
        list.addAll(voList);

        // 如果不是最大层级，继续向下查询
        int nextLevel = level + 1;
        if (nextLevel <= maxLevel) {
            voList.forEach(i -> listChild(list, i, nextLevel, maxLevel));
        }
    }
}
